﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GN.Pay.Core.Entities;
using GN.Pay.Core.Repositories;
using GN.Pay.Core.Exceptions;
using GN.Pay.TradeChannel;
using GN.Pay.TradeChannel.DefaultImpl;
using GN.Pay.Core.Runtime;
using GN.Pay.Core.Values.Services;
using GN.Pay.Core.Runtime.Caching;
using GN.Pay.Core.Values.Channel;
using GN.Pay.TradeChannel.ProtocolPay;

namespace GN.Pay.Core.Services.Impl
{
    /// <summary>
    /// 
    /// </summary>
    class ProtocolPayService : APayService, IProtocolPayService
    {
        private readonly IMerchantMemberBankCardRepository bankCardRepository;
        private readonly ITradeCacheService tradeCacheService;
        private readonly IPaySmsService paySmsService;
        private readonly IGenerateIdentifier generateIdentifier;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="bankCardRepository"></param>
        public ProtocolPayService(IMerchantMemberBankCardRepository bankCardRepository,
            ITradeCacheService tradeCacheService,
            IPaySmsService paySmsService,
            IGenerateIdentifier generateIdentifier)
        {
            this.bankCardRepository = bankCardRepository;
            this.tradeCacheService = tradeCacheService;
            this.paySmsService = paySmsService;
            this.generateIdentifier = generateIdentifier;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="bankNo"></param>
        /// <returns></returns>
        private string GetChannelId(string bankNo)
        {
            switch (bankNo.ToLowerInvariant())
            {
                case "gyb":
                    return "Gyb.ProtocolPay";
                default:
                    throw new NotSupportedException("不支持的银行");

            }
        }

        /// <summary>
        /// 商户会员绑卡申请
        /// </summary>
        /// <param name="appId"></param>
        /// <param name="bankCard"></param>
        /// <returns></returns>
        public string MerchantMemberBankCardBindApply(MerchantMemberBankCardBindApplyInfo apply)
        {
            string channelId = GetChannelId(apply.BankNo);
            var item = bankCardRepository.FirstOrDefault(s => s.MemberId == apply.MemberId
            && s.ChannelId == channelId && s.AccountNo == apply.AccountNo);
            if (item != null && item.State == 1)
            {
                PayExceptionUtils.ThrowPayValidationException("相同的卡已被绑定，不能重复绑定.");
            }
            var app = tradeCacheService.GetMerchantAppInfo(apply.AppId);
            if (app == null)
            {
                PayExceptionUtils.ThrowPayValidationException("应用不存在或已下架。");
            }
            var smsTemplate = app.SmsTemplates.FirstOrDefault(s => s.SmsType == 1);
            if (smsTemplate == null)
            {
                PayExceptionUtils.ThrowPayValidationException("无法绑定，未指定配置短信模板。");
            }
            var channel = tradeCacheService.GetTradeChannel(apply.AppId, channelId);
            if (channel == null)
            {
                PayExceptionUtils.ThrowPayValidationException("支付方式不存在或已下架。");
            }
            IProtocolPay protocolPay = channel as IProtocolPay;
            if (protocolPay == null)
            {
                PayExceptionUtils.ThrowPayValidationException("支付方式不支持协议绑卡操作。");
            }
            var bindApply = apply.CreateShortcutBindApply();
            if (!protocolPay.BankCardBindValidate(bindApply))
            {
                PayExceptionUtils.ThrowPayValidationException("绑定验证未通过");
            }           
            var checkCode = generateIdentifier.GenerateSmsCheckCode();
            var checkCodeHash = generateIdentifier.GenerateAppHashSmsCheckCode(app, checkCode);
            var token = Guid.NewGuid().ToString("N");
            var applyToken = apply.CreateToken(checkCodeHash, channelId, 5);
            tradeCacheService.SetMerchantMemberBankCardBindToken(token, applyToken);
            var content = new ShortcutBindSmsContent()
            {
                AppName = app.AppName,
                CardNo = apply.AccountNo,
                CheckCode = checkCode,
                ExpireMinute = applyToken.SmsExpireMinute,
                MobilePhone = apply.MobilePhone,
                RealName = apply.AccountName,
                SmsTemplate = smsTemplate.SmsTemplate
            };
            paySmsService.ShortcutBind(content);
            return token;
        }

        /// <summary>
        /// 绑定
        /// </summary>
        /// <param name="bindInfo"></param>
        public MerchantMemberBankCard MerchantMemberBankCardBind(MerchantMemberBankCardBindInfo bindInfo)
        {
            var appToken = tradeCacheService.GetMerchantMemberBankCardBindToken(bindInfo.Token);
            if (appToken == null)
            {
                PayExceptionUtils.ThrowPayValidationException("验证码已过期或票据无效。");
            }
            var app = tradeCacheService.GetMerchantAppInfo(appToken.AppId);
            if (app == null)
            {
                PayExceptionUtils.ThrowPayValidationException("应用不存在或已下架。");
            }
            var checkCodeHash = generateIdentifier.GenerateAppHashSmsCheckCode(app, bindInfo.SmsCheckCode);
            if (!checkCodeHash.Equals(appToken.SmsCheckCodeHash))
            {
                PayExceptionUtils.ThrowPayValidationException("短信验证码不正确。");
            }
            var item = bankCardRepository.FirstOrDefault(s => s.MemberId == appToken.MemberId
                         && s.ChannelId == appToken.ChannelId && s.AccountNo == appToken.AccountNo);
            if (item != null && item.State == 1)
            {
                return item;
            }
            var channel = tradeCacheService.GetTradeChannel(appToken.AppId, appToken.ChannelId);
            if (channel == null)
            {
                PayExceptionUtils.ThrowPayValidationException("支付方式不存在或已下架。");
            }
            IProtocolPay protocolPay = channel as IProtocolPay;
            if (protocolPay == null)
            {
                PayExceptionUtils.ThrowPayValidationException("支付方式不支持协议绑卡操作。");
            }
            var bindResult = protocolPay.BankCardBind(appToken.CreateShortcutBindApply());
            if (bindResult.State == ProtocolApplyResultState.Success)
            {
                if (item == null)
                {
                    item = new MerchantMemberBankCard()
                    {
                        AccountName = bindResult.AccountName,
                        AccountNo = bindResult.AccountNo,
                        AccountType = (int)bindResult.AccountType,
                        BankNo = bindResult.BankNo,
                        ChannelId = appToken.ChannelId,
                        CreditCardLastNo = bindResult.CreditCardLastNo,
                        CreditCardLastTerm = bindResult.CreditCardLastTerm,
                        IdentityNo = bindResult.IdentityNo,
                        MemberId = appToken.MemberId,
                        MobilePhone = bindResult.MobilePhone,
                        OpenBankCity = bindResult.OpenBankCity,
                        OpenBankName = bindResult.OpenBankName,
                        OpenBankProvince = appToken.OpenBankProvince,
                        ProtocolNo = bindResult.ProtocolNo,

                        State = 1,
                        ChannelMemberNo = bindResult.ChannelMemberNo,
                        OperationTime = DateTime.Now
                    };
                    bankCardRepository.Insert(item);
                }
                else
                {
                    item.State = 1;
                    item.OperationTime = DateTime.Now;
                    bankCardRepository.Update(item);
                }
                bankCardRepository.SaveChanges();
                tradeCacheService.RemoveMerchantMemberBankCardBindToken(bindInfo.Token);
                return item;
            }
            else
            {
                throw PayExceptionUtils.ThrowPayValidationException(bindResult.StateMessage);
            }
        }
    }
}
